﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;
using System.Globalization;
using SharpFileZillaStats.Properties;

namespace SharpFileZillaStats {
    public partial class DownloadCharts : UserControl {

        private enum StatsChartType {
            Columns,
            Lines,
            Areas
        
        }

        private enum StatsGrouping { 
            none,
            IP,
            File,
            Login,
            Country,
            Region,
            City
        }


        private enum StatsCountInterval { 
            Day = 1,
            Week = 2,
            Month = 3,
            Year = 4
        }

        public StatsReader SR {get; set;}
        private int maxGroups= 10;
      
        
        public DownloadCharts() {
            InitializeComponent();
            this.init();
        }

        private void init() {
            diagramCbChartType.Items.AddRange(Enum.GetNames(typeof(StatsChartType)));
            diagramCbInterval.Items.AddRange(Enum.GetNames(typeof(StatsCountInterval)));
            diagramCbGrouping.Items.AddRange(Enum.GetNames(typeof(StatsGrouping)));
            diagramCbChartType.SelectedIndex = 0;
            diagramCbGrouping.SelectedIndex = 0;
            diagramCbInterval.SelectedIndex = 0;
        }


        private T parseEnum<T>(ToolStripComboBox comboBox) {
            return (T)Enum.Parse(typeof(T), comboBox.SelectedItem.ToString());
        }
        private T parseEnum<T>(ComboBox comboBox) {
            return (T)Enum.Parse(typeof(T), comboBox.SelectedItem.ToString());
        }

        private DateTime FirstDateOfWeek(int year, int weekOfYear) {
            DateTime jan1 = new DateTime(year, 1, 1);

            int daysOffset = (int)CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek - (int)jan1.DayOfWeek;

            DateTime firstMonday = jan1.AddDays(daysOffset);

            int firstWeek = CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(jan1, CultureInfo.CurrentCulture.DateTimeFormat.CalendarWeekRule, CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek);

            if(firstWeek <= 1) {
                weekOfYear -= 1;
            }

            return firstMonday.AddDays(weekOfYear * 7);
        }

        private int getWeekNumber(DateTime dateTime) {
            CultureInfo ci = CultureInfo.CurrentCulture;
            return ci.Calendar.GetWeekOfYear(dateTime, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
        }


        public void drawDiagram(object sender, EventArgs e) {
            this.drawDiagram();
        }

        public void drawDiagram() {
            if(SR == null) return;
            this.Chart.Series.Clear();
            if(!(SR.DataView.Count != 0 
                 && diagramCbGrouping.SelectedIndex >= 0 
                 && diagramCbInterval.SelectedIndex >= 0
                 && diagramCbChartType.SelectedIndex >= 0)) return;

            ChartArea ca = this.Chart.ChartAreas[0];
            String chartTitle = "";
            String legendTitle = "Downloads";
            DateTimeIntervalType chartingInterval = DateTimeIntervalType.NotSet;
            StatsGrouping grouping = parseEnum<StatsGrouping>(diagramCbGrouping);
            StatsChartType chartType = parseEnum<StatsChartType>(diagramCbChartType);

            StatsCountInterval countInterval = parseEnum<StatsCountInterval>(diagramCbInterval);
            switch(countInterval) {
                case StatsCountInterval.Day:
                    chartTitle = "Downloads per Day";
                    chartingInterval = DateTimeIntervalType.Days;
                    //ca.AxisX.Interval = new TimeSpan(1, 0, 0, 0).TotalDays;
                    break;
                case StatsCountInterval.Week:
                    chartTitle = "Downloads per Week";
                    
                    chartingInterval = DateTimeIntervalType.Weeks;
                    //ca.AxisX.Interval = new TimeSpan(7, 0, 0, 0).TotalDays;
                    break;
                case StatsCountInterval.Month:
                    chartTitle = "Downloads per Month";
                    chartingInterval = DateTimeIntervalType.Months;
                    //ca.AxisX.Interval = new TimeSpan(15, 0, 0, 0).TotalDays;
                    break;
                case StatsCountInterval.Year:
                    chartTitle = "Downloads per Year";
                    chartingInterval = DateTimeIntervalType.Years;
                    //ca.AxisX.Interval = new TimeSpan(365, 0, 0, 0).TotalDays;
                    break;
            }
            
            
            SeriesChartType seriesChartType = SeriesChartType.Column;
            switch(chartType) {
                case StatsChartType.Lines: seriesChartType= SeriesChartType.Line; break;
                case StatsChartType.Areas: seriesChartType = SeriesChartType.Area; break;
                default: seriesChartType = SeriesChartType.Column; break;
            
            }

            if(grouping == StatsGrouping.none) {
                var dates  =  from row in SR.DataView.ToTable().AsEnumerable()
                              orderby row.Field<DateTime>(SR.ColumnDTG) 
                              select row.Field<DateTime>(SR.ColumnDTG);
                Series series = getSeries("Downloads", dates.ToArray(), countInterval, seriesChartType);
                
                drawSeries(series, chartingInterval);
            } else {
                String groupColumn = "";
                switch(grouping) {
                    case StatsGrouping.File: 
                        groupColumn = SR.ColumnFile;
                        legendTitle = "Files";
                        break;
                    case StatsGrouping.Login: 
                        groupColumn = SR.ColumnLogin;
                        legendTitle = "Login Role";
                        break;
                    case StatsGrouping.IP: 
                        groupColumn = SR.ColumnIP;
                        legendTitle = "IP";
                        break;
                    case StatsGrouping.Country: 
                        groupColumn = SR.ColumnCountry;
                        legendTitle = "Country";
                        break;
                    case StatsGrouping.Region: 
                        groupColumn = SR.ColumnRegion;
                        legendTitle = "Region";
                        break;
                    case StatsGrouping.City: 
                        groupColumn = SR.ColumnCity; 
                        legendTitle = "City";
                        break;
                    default: break;
                }


                var dateGroups  =  from row in SR.DataView.ToTable().AsEnumerable()
                                   orderby row.Field<DateTime>(SR.ColumnDTG) 
                                   group row.Field<DateTime>(SR.ColumnDTG)
                                   by row.Field<String>(groupColumn) into g
                                   select new {
                                       groupName = g.Key,
                                       dates = g
                                   };

                int i = 0;
                foreach(var dateGroup in dateGroups) {
                    if(i >= this.maxGroups) break;
                    Series series = getSeries(dateGroup.groupName, dateGroup.dates.ToArray(), countInterval, seriesChartType);
                    drawSeries(series, chartingInterval);
                    
                    i++;
                }
               
            }
            //Add legend title
            Legend l = this.Chart.Legends[0];
            l.Title = legendTitle;
            l.TitleAlignment = StringAlignment.Near;
            
            

        }

        private Series getSeries(String seriesName, DateTime[] dates, StatsCountInterval timeInterval, SeriesChartType chartType) {
            Series series = this.getEmptySeries(seriesName);
            series.ChartType = chartType;
            if(diagramCbInterval.SelectedIndex >= 0) {
                Dictionary<DateTime, int> histogram = new Dictionary<DateTime, int>();
                TimeSpan diff = new TimeSpan();
                List<DateTime> dt = null;
                DateTimeIntervalType dateTimeInterval = DateTimeIntervalType.Auto;

                DateTime dateMin = dates.Min();
                DateTime dateMax = dates.Max();
                switch(timeInterval) {
                    case StatsCountInterval.Day : dt = (from date in dates
                                        select new DateTime(date.Year, date.Month, date.Day)
                                       ).ToList();
                        seriesName = "Downloads";
                        dateTimeInterval = DateTimeIntervalType.Days;
                        dateMin = dateMin.AddDays(-1);
                        dateMax = dateMax.AddDays(1);
                        diff = new TimeSpan(1, 0, 0, 0);
                        break;
                    case StatsCountInterval.Week: dt = (from date in dates
                                         select this.FirstDateOfWeek(date.Year, this.getWeekNumber(date))
                                        ).ToList();
                        seriesName = "Downloads";
                        dateTimeInterval = DateTimeIntervalType.Weeks;
                        dateMin = dateMin.AddDays(-7);
                        dateMax = dateMax.AddDays(7);
                        diff = new TimeSpan(7, 0, 0, 0);
                        break;
                    case StatsCountInterval.Month: dt = (from date in dates
                                          select new DateTime(date.Year, date.Month, 15)
                                        ).ToList();
                        seriesName = "Downloads";
                        dateTimeInterval = DateTimeIntervalType.Months;
                        dateMin = dateMin.AddMonths(-1);
                        dateMax = dateMax.AddMonths(1);
                        diff = new TimeSpan(30, 0, 0, 0, 0);
                        break;
                    case StatsCountInterval.Year: dt = (from date in dates
                                         select new DateTime(date.Year, 6, 15)
                                       ).ToList();
                        seriesName = "Downloads";
                        dateMin = dateMin.AddYears(-1);
                        dateMax = dateMax.AddYears(1);
                        dateTimeInterval = DateTimeIntervalType.Years;
                        diff = new TimeSpan(365, 0, 0, 0, 0);
                        break;
                }
                //diff = new TimeSpan(1, 0, 0, 0, 0);

                dt.Add(dateMin);
                dt.Add(dateMax);

                var xValues = (from date in dt
                               orderby date
                               select date).Distinct();
                
                var yValues = (from date in xValues
                               select dt.Count(p => p.ToOADate() == date.ToOADate()));

                var xValues2 = (new DateTime[] { dateMin }).Concat(xValues).Concat(new DateTime[] { dateMax });
                var yValues2 = (new Int32[] { 0 }).Concat(yValues).Concat(new Int32[] { 0 });
                
                DateTime[] xDates = xValues2.ToArray();
                Int32[] yCounts   = yValues2.ToArray();
                
                series.Points.DataBindXY(xDates,yCounts);
                switch(timeInterval) { 
                    case StatsCountInterval.Day :
                        for(int i=0; i< yCounts.Length; i++) {
                            series.Points[i].ToolTip = String.Format("{0}\n{1:dd}. {1:MMMM}\n{2} downloads", series.Name, xDates[i], yCounts[i]);
                        }
                        break;

                    case StatsCountInterval.Week:
                        for(int i=0; i< yCounts.Length; i++) {
                            series.Points[i].ToolTip = String.Format("{0}\nKW{1:00}:\n{2} downloads", series.Name, this.getWeekNumber(xDates[i]), yCounts[i]);
                        }
                        break;


                    case StatsCountInterval.Month:
                        for(int i=0; i< yCounts.Length; i++) {
                            series.Points[i].ToolTip = String.Format("{0}\n{1:MMMM}:\n{2} downloads", series.Name, xDates[i], yCounts[i]);
                        }
                        break;

                    case StatsCountInterval.Year:
                        for(int i=0; i< yCounts.Length; i++) {
                            series.Points[i].ToolTip = String.Format("{0}\n{1:yyyy}:\n{2} downloads", series.Name, xDates[i], yCounts[i]);
                        }
                        break;
                }
                

         
                //Double xMin = Math.Min(dates.Min().ToOADate(), xValues.Select(p => p.ToOADate()).Min());
                //Double xMax = Math.Max(dates.Max().ToOADate(), xValues.Select(p => p.ToOADate()).Max());

                //Double xMin = dates.Min().ToOADate();
                //Double xMax = dates.Max().ToOADate();


            }

            return series;
        }

        private void drawSeries(Series series, DateTimeIntervalType dtIntervalType) {
            if(series == null) return;
            ChartArea CA = this.Chart.ChartAreas[0];
            CA.AxisX.IntervalType = dtIntervalType;
            // CA.AxisX.Minimum = xMin;
            // CA.AxisX.Maximum = xMax;
            // CA.AxisY.Minimum = 0;
            // CA.AxisY.Maximum = yValues.Max() + yValues.Max() * 0.1;

            //this.chart1.ChartAreas[0].AxisX.LabelStyle.Format = "MM:DD";

            this.Chart.Series.Add(series);
            
        }

        private Series getEmptySeries(String name) {
            Series series = new Series(name);
            series.ToolTip = name;
            series.XValueType = ChartValueType.Date;
            series.SetCustomProperty("PointWidth", "0.8");
            series.SetCustomProperty("DrawSideBySide", "true");
            return series;
        }

        private void toolStripButton1_Click(object sender, EventArgs e) {

            //SettingsWindow w = new SettingsWindow(this.Chart.Series);
            //w.Show();
        }


        private void saveAsImageToolStripMenuItem_Click(object sender, EventArgs e) {
            SaveFileDialog SFD = new SaveFileDialog();
            SFD.Filter = "PNG (*.png)|*.png";
            if(SFD.ShowDialog() == DialogResult.OK){
                this.Chart.SaveImage(SFD.FileName, ChartImageFormat.Png);
            
            }

            
        }

        private void Chart_MouseClick(object sender, MouseEventArgs e) {
            if(e.Button == System.Windows.Forms.MouseButtons.Right) {
                this.contextChart.Show((Control)sender, e.Location);
            
            
            }
        }

        private void chartAreaToolStripMenuItem_Click(object sender, EventArgs e) {
            new SettingsWindow<ChartArea>(this.Chart.ChartAreas[0]).Show();
        }

        private void legendToolStripMenuItem_Click(object sender, EventArgs e) {
            new SettingsWindow<Legend>(this.Chart.Legends[0]).Show();
        }

        




    }
}
